package matsk.mszdqabbs.Service.Impl;

import matsk.mszdqabbs.Configuration.conf.imageResourceMapper;
import matsk.mszdqabbs.DAO.AnswerDAO;
import matsk.mszdqabbs.DAO.ArticleDAO;
import matsk.mszdqabbs.DAO.FollowDAO;
import matsk.mszdqabbs.DAO.UserDAO;
import matsk.mszdqabbs.IO.CustomIO;
import matsk.mszdqabbs.Pojo.User;
import matsk.mszdqabbs.Service.UserService;
import matsk.mszdqabbs.Utils.*;
import matsk.mszdqabbs.Verify.UserVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDAO userDAO;
    @Autowired
    private UserVerifier userVerifier;
    @Autowired
    private CustomIO customIO;
    @Autowired
    private AnswerDAO answerDAO;
    @Autowired
    private FollowDAO followDAO;
    @Autowired
    private ArticleDAO articleDAO;

    @Override
    @Transactional
    public String handleRegisterMailRequest(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> resultMap = new HashMap<>();
        String emailInput = request.getParameter("emailinput");
        //如果邮件地址已存在，则无法使用该邮箱注册
        if(userDAO.emailExistsCheck(emailInput) > 0) {
            resultMap.put("success","false");
            resultMap.put("reason","邮件地址已被注册！");
        } else {
            //随机生成6位数字
            int autoGeneratedNumber = (int)(Math.random() * 900000 + 100000);
            //保存到用户Session
            request.getSession().setAttribute("registerEmailVerifyNumber",
                    autoGeneratedNumber + "");
            try {
                //发送邮件
                EmailSender.sendEmail(
                        "感谢您注册码上知道！",
                        "您的验证码是：<h2>" + autoGeneratedNumber
                                + "</h2><br />请您在十分钟内在网页输入验证码，继续完成后续注册操作。<br />"
                                + "请勿把验证码告诉他人。<br /><br />"
                                + "——码上知道问答论坛——",
                        emailInput);
                //暂时保存用户的邮箱到session
                request.getSession().setAttribute("emailinput",emailInput);
                //返回成功
                resultMap.put("success","true");
            } catch (Exception e) {
                e.printStackTrace();
                resultMap.put("success","false");
                resultMap.put("reason","抱歉，暂时无法发送邮件！请联系管理员。");
            }
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    public String checkVerifyCode(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> resultMap = new HashMap<>();
        if(request.getParameter("verifycodeinput")
                .equals(request.getSession()
                .getAttribute("registerEmailVerifyNumber"))) resultMap.put("success","true");
        else resultMap.put("success","false");

        return JacksonUtils.mapToJson(resultMap);

    }

    @Transactional
    public String doRegister(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> resultMap = new HashMap<>();
        String emailaddress = (String) request.getSession().getAttribute("emailinput");
        if (emailaddress == null) {
            resultMap.put("success", "false");
            resultMap.put("reason", "未进行邮箱验证！");
        } else if (userDAO.emailExistsCheck(emailaddress) > 0) {
            resultMap.put("success", "false");
            resultMap.put("reason", "邮件地址已被注册！");
        } else if (request.getParameter("username") == null
                || userDAO.usernameExistsCheck(request.getParameter("username")) > 0) {
            resultMap.put("success", "false");
            resultMap.put("reason", "用户名已被注册！");
        } else if (request.getParameter("nickname") == null
                || userDAO.nicknameExistsCheck(request.getParameter("nickname")) > 0) {
            resultMap.put("success", "false");
            resultMap.put("reason", "昵称已被注册！");
        } else {
            User newUser = new User(
                    0,
                    request.getParameter("username"),
                    request.getParameter("password"),
                    emailaddress,
                    request.getParameter("nickname"),
                    //这些等到用户注册之后可以自行设置，或保持为空（使用默认的配置）
                    "这个人有点懒，没有留下个性签名哦~",
                    null,
                    null,
                    null,
                    new Timestamp(System.currentTimeMillis()),
                    null,
                    new Timestamp(System.currentTimeMillis()),
                    null
            );
            //后端也要验证输入格式的合法性
            if (userVerifier.UserRegisterVerify(newUser)) {
                //对用户密码进行Sha256加密
                newUser.setEncrypted_password(Sha256.getSHA256(newUser.getEncrypted_password()));
                //存入数据库
                if (userDAO.register(newUser) == 1) {
                    resultMap.put("success", "true");
                }
                //实现注册完自动登录，签发token并加入返回体
                response.addCookie(signToken(userDAO.getAutoIncrement()));
            } else {
                resultMap.put("success", "false");
                resultMap.put("reason", "未经授权访问接口！");
            }
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    @Transactional
    public String doLogin(String username,
                          String password,
                          HttpServletRequest request,
                          HttpServletResponse response) {
        Map<String, String> resultMap = new HashMap<>();
        //登录失败就会直接返回resultMap
        resultMap.put("success","false");
        //判断是否被锁定
        if(request.getSession().getAttribute("lockTime") == null) {
            List<User> u;
            boolean isLoginSuccess = false;
            //通过用户名是否包含@来判断用户输入的是邮箱还是用户名
            //注册时已经不允许用户名中出现@等特殊字符
            if(username.contains("@")) {
                if((u = userDAO.loginWithEmail(username, Sha256.getSHA256(password))).size() == 1) {
                    isLoginSuccess = true;
                }
            } else {
                if((u = userDAO.loginWithUsername(username, Sha256.getSHA256(password))).size() == 1) {
                    isLoginSuccess = true;
                }
            }
            if(isLoginSuccess) {
                //登录成功
                resultMap.put("success","true");
                request.getSession().removeAttribute("failCount");
                //签发Token加入返回体
                response.addCookie(signToken(u.get(0).getId()));
            } else {
                //设定错误计数，超过5次禁止登录30秒
                Integer failCount = (Integer)request.getSession().getAttribute("failCount");
                if(failCount == null) {
                    request.getSession().setAttribute("failCount",1);
                } else {
                    request.getSession().setAttribute("failCount",++failCount);
                }
                if(failCount != null && failCount == 5) {
                    //错误超过5次，记录时间并锁定
                    request.getSession().setAttribute("lockTime",System.currentTimeMillis());
                    //返回锁定时间（单位：秒），让前端等待
                    resultMap.put("timeToWait","10");
                    //清空错误计数
                    request.getSession().removeAttribute("failCount");
                }
            }
        } else {
            System.err.println(System.currentTimeMillis());
            System.err.println(request.getSession().getAttribute("lockTime"));
            //锁定10秒
            if(System.currentTimeMillis()
                    - (long)request.getSession().getAttribute("lockTime")
                    > 10000) {
                //解除锁定
                request.getSession().removeAttribute("lockTime");
                //重新登录
                return doLogin(username, password, request, response);
            } else {
                //刷新需要等待的时间（单位：秒）
                resultMap.put("timeToWait", ((10000 - (System.currentTimeMillis()
                        - (long)request.getSession().getAttribute("lockTime")))  / 1000 ) + "");
            }
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    @Transactional
    public String getUser(String uid, HttpServletRequest request) {
        try {
            List<User> userList = userDAO.getUser(Integer.parseInt(uid));
            if(userList.size() > 1) throw new Exception("存在重名用户！");
            else if(updateLoginInfo(request, uid) == 0 || userList.size() == 0)
                throw new Exception("用户不存在！");
            else return JacksonUtils.obj2json(userList.get(0));
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 签发用户token
     * @param userId
     * @return
     */
    private Cookie signToken(Integer userId) {
        //签发Token
        String token = TokenUtils.sign(userId);
        //token要保存到cookie中
        Cookie tokenCookie = new Cookie("token", token);
        //Token有效期一年
        tokenCookie.setMaxAge(365 * 24 * 60 * 60);
        tokenCookie.setPath("/");
        return tokenCookie;
    }


    /**
     * 在客户端登录过之后通过id来更新用户的登录信息
     * 包括最后登录的IP和时间
     * @param uid
     * @return
     */
    private int updateLoginInfo(HttpServletRequest request, String uid) {
        Integer id = Integer.parseInt(uid);
        Timestamp last_login_time = new Timestamp(System.currentTimeMillis());
        String last_login_ip = RequestUtils.getIPAddress(request);
        return userDAO.updateLoginInfoById(id, last_login_time, last_login_ip);
    }

    @Override
    @Transactional
    public String updateNickname(String uid, String newNickname, HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("success","false");
        Integer id = Integer.parseInt(uid);
        //需要验证是否登录
        if(TokenUtils.isLogin(request) && userDAO.updateNickname(id, newNickname) == 1) {
            resultMap.put("success", "true");
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    @Transactional
    public String updateMotto(String uid, String newMotto, HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("success","false");
        Integer id = Integer.parseInt(uid);
        //需要验证是否登录
        if(TokenUtils.isLogin(request) && userDAO.updateMotto(id, newMotto) == 1) {
            resultMap.put("success", "true");
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    @Transactional
    public String handleHeadPhotoUpdate(Integer uid, MultipartFile newHeadPhoto, HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("success", "false");
        if(uid != null && uid.equals(TokenUtils.getUid(request))) {
            //先查询数据库，获取该用户的信息
            List<User> user = userDAO.getUser(uid);
            if(user.size() == 1) {
                //该用户之前上传过图片
                if(user.get(0).getHead_photo_path() != null) {
                    //删除旧图片
                    File toRemove = new File(user.get(0).getHead_photo_path());
                    //删除失败，直接返回
                    if(!toRemove.delete()) return JacksonUtils.mapToJson(resultMap);
                }
                //图片允许的后缀名
                List<String> supportSuffix = new ArrayList<>();
                supportSuffix.add(".jpg");
                supportSuffix.add(".jpeg");
                supportSuffix.add(".png");
                supportSuffix.add(".gif");
                supportSuffix.add(".ico");
                Map<String, String> saveRes = customIO.doSave(imageResourceMapper.getHeadPhotoFileLocationByEnvironment(),
                        newHeadPhoto, supportSuffix);
                if(saveRes != null) {
                    if(userDAO.updateHeadPhotoInfo(uid,
                            saveRes.get("filePath"),
                            RequestUtils.getProjectRootUrl(request)
                                    //这里substring的原因是，项目根URL后不需要多余的/
                                    + imageResourceMapper.headPhotoUrlSuffix.substring(1)
                                    + saveRes.get("name"),
                            saveRes.get("name")) == 1) {
                        resultMap.put("success", "true");
                    }
                }
            }
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    public String handleFindPassword(String email, HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("success","false");
        if(userVerifier.EmailFormatVerify(email) && (userDAO.emailExistsCheck(email) == 1)) {
            //随机生成6位数字
            int autoGeneratedNumber = (int)(Math.random() * 900000 + 100000);
            //保存到用户Session
            request.getSession().setAttribute("findPasswordVerifyNumber",
                    autoGeneratedNumber + "");
            request.getSession().setAttribute("emailToFindPassword",email);
            try {
                EmailSender.sendEmail("码上知道：找回密码",
                        "用户您好，您正在试图找回账户" + email + "的密码，<br />您的验证码是：<br /><h2>"
                                + autoGeneratedNumber + "</h2><br />请您在十分钟内在网页输入验证码以进行后续操作。请不要把验证码告诉他人。"
                                + "<br /><br />——码上知道问答论坛——",email);
                resultMap.put("success","true");
            } catch (Exception e) {
                e.printStackTrace();
                resultMap.put("reason","抱歉，邮件暂时无法发送");
            }
        } else resultMap.put("reason","邮件地址未注册");
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    public String checkFindPasswordVerifyCode(String code, HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("success","false");
        if(request.getSession().getAttribute("findPasswordVerifyNumber") != null) {
            if(code.equals(request.getSession().getAttribute("findPasswordVerifyNumber") + "")) {
                request.getSession().setAttribute("findPasswordVerified","true");
                resultMap.put("success","true");
            }
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    @Transactional
    public String handlePasswordChangeRequest(String newPassword, HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("success","false");
        String emailToChange = request.getSession().getAttribute("emailToFindPassword") + "";
        String isCodeVerified = request.getSession().getAttribute("findPasswordVerified") + "";
        //合法性检查
        if(userVerifier.EmailFormatVerify(emailToChange) && isCodeVerified.equals("true")) {
            //邮箱重复检查
            if(userDAO.emailExistsCheck(emailToChange) == 1) {
                //执行修改
                if(userDAO.updatePassword(emailToChange,
                        Sha256.getSHA256(newPassword)) == 1) {
                    resultMap.put("success","true");
                }
            }
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    /**
     * 获取要展示在文章或回答展示页右边的作者信息
     * 包括：用户ID、昵称、个性签名、
     * 头像URL、回答数、文章数、粉丝数
     * 设置为default权限是因为只允许其他Service层对象调用
     * @return Map对象，包含了返回信息的键值对
     */
    @Transactional
    public Map<String, Object> getUserInfoToShowAside(Integer userId) throws Exception {
        Map<String, Object> resultMap = new HashMap<>();
        List<User> user = userDAO.getUser(userId);
        if(user != null && user.size() == 1) {
            Integer answerCount = answerDAO.getAnswerCountByUserId(userId);
            Integer articleCount = articleDAO.getArticleCountWriteBy(userId);
            Integer followerCount = followDAO.getFollowerCount(userId);
            resultMap.put("authorId",userId);
            resultMap.put("headPhotoUrl",user.get(0).getHead_photo_url());
            resultMap.put("nickname",user.get(0).getNickname());
            resultMap.put("motto",user.get(0).getMotto());
            resultMap.put("answerCount",answerCount);
            resultMap.put("articleCount",articleCount);
            resultMap.put("followerCount",followerCount);
        } else throw new Exception("存在重名用户或用户不存在！");
        return resultMap;
    }

    @Override
    @Transactional
    public String toggleFollow(Integer to_follow, HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("success","false");
        Integer follower = TokenUtils.getUid(request);
        if(follower != null && !follower.equals(to_follow)) {//不允许自己关注自己
            if(followDAO.isAlreadyFollow(follower,to_follow) >= 1) {
                if(followDAO.cancel(follower, to_follow) == 1) {
                    resultMap.put("success","true");
                    resultMap.put("type","cancel");
                }
            } else {
                if(followDAO.follow(follower, to_follow) == 1) {
                    resultMap.put("success","true");
                    resultMap.put("type","follow");
                }
            }
        }
        return JacksonUtils.mapToJson(resultMap);
    }

    /**
     * 获得评论者的信息，包括评论者的ID、头像、昵称
     * @return Map对象，包括评论者的ID、头像、昵称
     */
    @Override
    @Transactional
    public Map<String, Object> getUserInfoOfComment(Integer userId) throws Exception {
        Map<String, Object> resultMap = new HashMap<>();
        List<User> user = userDAO.getUser(userId);
        if(user != null && user.size() == 1) {
            resultMap.put("authorId",userId);
            resultMap.put("headPhotoUrl",user.get(0).getHead_photo_url());
            resultMap.put("nickname",user.get(0).getNickname());
        } else throw new Exception("存在重名用户或用户不存在！");
        return resultMap;
    }

    /**
     * 获取访问主页时需要的基本信息，
     * 包括用户名、昵称、个性签名、头像URL
     * @param visitUserId 访问用户的ID
     * @return 访问用户的信息，包括用户名、昵称、个性签名、头像URL
     */
    @Override
    @Transactional
    public String getVisitUserBasicInfo(Integer visitUserId, HttpServletRequest request) throws Exception {
        Map<String, Object> resultMap = new HashMap<>();
        List<User> visitUser = userDAO.getUser(visitUserId);
        if(visitUser != null && visitUser.size() == 1) {
            resultMap.put("visitUserId",visitUserId);
            resultMap.put("username",visitUser.get(0).getUsername());
            resultMap.put("motto",visitUser.get(0).getMotto());
            resultMap.put("headPhotoUrl",visitUser.get(0).getHead_photo_url());
            resultMap.put("nickname",visitUser.get(0).getNickname());
            resultMap.put("registerTime",visitUser.get(0).getRegister_time());
            //用户未登录情况下，默认未关注
            resultMap.put("isAlreadyFollow","false");
            Integer loginUid = TokenUtils.getUid(request);
            if(loginUid != null) {
                //是否已经关注作者
                if (followDAO.isAlreadyFollow(loginUid, visitUserId) >= 1) {
                    resultMap.put("isAlreadyFollow", "true");
                }
            }
        } else throw new Exception("存在重名用户或用户不存在！");
        return JacksonUtils.mapToJson(resultMap);
    }

    @Override
    public String getFollowersOf(Integer uid) {
        List<User> followers = followDAO.getFollowersOf(uid);
        List<Map<String, Object>> resultList = new ArrayList<>();
        if(followers != null && followers.size() > 0) {
            //需要的是id、头像URL和昵称
            for(User u : followers) {
                Map<String, Object> eachFollower = new HashMap<>();
                eachFollower.put("followerId", u.getId());
                eachFollower.put("headPhotoUrl", u.getHead_photo_url());
                eachFollower.put("nickname", u.getNickname());
                resultList.add(eachFollower);
            }
        }
        try {
            return JacksonUtils.obj2json(resultList);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String getFollowsOf(Integer uid) {
        List<User> to_follows = followDAO.getFollowsOf(uid);
        List<Map<String, Object>> resultList = new ArrayList<>();
        if(to_follows != null && to_follows.size() > 0) {
            //需要的是id、头像URL和昵称
            for(User u : to_follows) {
                Map<String, Object> eachFollow = new HashMap<>();
                eachFollow.put("followerId", u.getId());
                eachFollow.put("headPhotoUrl", u.getHead_photo_url());
                eachFollow.put("nickname", u.getNickname());
                resultList.add(eachFollow);
            }
        }
        try {
            return JacksonUtils.obj2json(resultList);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
